<?php
/**
 * Injector.php created on 12 mai 08 
 * Author: Rodrigue Le Gall
 *
 * RPortal is an open source web based Content Management System
 * Copyright (C) 2002-2008  Rodrigue Le Gall
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 * 
 * @category RPortal2
 * @package  RPortal_Core
 * @author   Rodrigue Le Gall <r.legall@rportal.org>
 * @license  http://www.gnu.org/licenses/gpl-2.0.html GPLv2
 * @link     http://pear.rportal.org/RPortal_Core
 */
 
 declare (ENCODING = 'utf-8');

/**
 * This is the main class that implements the light Ioc Pattern
 * 
 * @category RPortal2
 * @package  RPortal_Core
 * @author   Rodrigue Le Gall <r.legall@rportal.org>
 * @license  http://www.gnu.org/licenses/gpl-2.0.html GPLv2
 * @link     http://pear.rportal.org/RPortal_Core
 */
class Injector
{
    public static $dbhandle = null;

    /**
     * get the internal database handler
     * 
     * @return SQLiteDatabase a database handler
     */
    protected static function getDB()
    {
        if (Injector :: $dbhandle == null) {
            Injector :: $dbhandle = sqlite_factory(dirname(__FILE__) . '/kernel.db');
            Injector :: $dbhandle->query("PRAGMA synchronous=OFF");
        }
        return Injector :: $dbhandle;
    }

    /**
     * initialize the database for IoC features
     * 
     * @return void
     * @author Rodrigue Le Gall <r.legall@rportal.org>
     */
    public static function initDB()
    {
        $dbhandle = Injector :: getDB();
        // Create table for class
        $dbhandle->query("DROP TABLE Class");
        $dbhandle->query("CREATE TABLE Class ( name VARCHAR(255), file VARCHAR(255), date DATETIME )");

        // Create table for injection
        $dbhandle->query("DROP TABLE Injection");
        $dbhandle->query("CREATE TABLE Injection ( key VARCHAR(255), class VARCHAR(255), file VARCHAR(255), date DATETIME )");
    }

    /**
     * Add a class in the IoC container.
     * <p>An injection is automaticaly created for this class in order to be
     * able to create a new instance for this class by using the name of the
     * class as key.
     * 
     * @param String $className Name of the class
     * @param String $filePath  real file path containing the class
     * 
     * @return void
     * 
     * @author Rodrigue Le Gall <r.legall@rportal.org>
     */
    public static function addClass($className, $filePath)
    {
        if (!is_file($filePath)) {
            throw new RPFileNotFoundException($filePath);
        }
        $dbhandle = Injector :: getDB();
        $dbhandle->query("insert into Class (name,file,date) VALUES ('$className','$filePath',datetime('now'))");
        Injector :: addInjection($className, $className);
        return true;
    }

    /**
     * add an interface in the IoC container
     * 
     * @param String $className the name of the interface
     * @param String $filePath  real file path containing the class
     * 
     * @return void
     * @author Rodrigue Le Gall <r.legall@rportal.org>
     */
    public static function addInterface($className, $filePath)
    {
        if (!is_file($filePath)) {
            throw new RPFileNotFoundException($filePath);
        }
        $dbhandle = Injector :: getDB();
        $dbhandle->query("insert into Class (name,file,date) VALUES ('$className','$filePath',datetime('now'))");
        return true;
    }

    /**
     * add an injection
     * <p> an injection is a relation between a key and a class name.
     * <p> an injection is unique.
     * 
     * @param String $key       the key of the injection
     * @param String $className the name of a class or interface
     * 
     * @return boolean true if the add is done
     * @throws RPUndefinedClassException
     * @author Rodrigue Le Gall <r.legall@rportal.org>
     */
    public static function addInjection($key, $className)
    {
        $file = Injector :: getFile($className);
        $dbhandle = Injector :: getDB();
        $dbhandle->query("delete from Injection where key='$key'");
        $dbhandle->query("insert into Injection (key,class,file,date) VALUES ('$key','$className','$file',datetime('now'))");
        return true;
    }

    /**
     * return the class for the specified key
     * 
     * @param String $key the researched key
     * 
     * @return String the class name
     * @throws RPUndefinedClassException
     * @author Rodrigue Le Gall <r.legall@rportal.org>
     */
    public static function getClass($key)
    {
        $dbhandle = Injector :: getDB();
        $result = $dbhandle->query("select class from Injection where key='$key'");
        if ($line = $result->fetch()) {
            return $line['class'];
        }
        throw new RPUndefinedClassException('The key ' . $key . ' is undefined. No class is associated with it.');
    }

    /**
     * return an object instance for the specified injection
     * 
     * @param String $key the key of injection
     * 
     * @return Object an object instance
     * @throws RPUndefinedClassException
     * @author Rodrigue Le Gall <r.legall@rportal.org>
     */
    public static function getObject($key)
    {
        $className = Injector :: getClass($key);
        $arg_list = func_get_args();
        if (count($arg_list) == 1) {
            return new $className ();
        }
        $args = array ();
        for ($i = 1; $i < count($arg_list); $i++) {
            $args[] = '$arg_list[' . $i . ']';
        }
        $cmd = '$exitObject = new ' . $className . '(' . implode(',', $args) . ');';
        $result = eval ('$exitObject = new ' . $className . '(' . implode(',', $args) . ');');
        return $exitObject;
    }

    /**
     * delete the specified class
     * 
     * @param String $className the class or interface name
     * 
     * @return void
     * @author Rodrigue Le Gall <r.legall@rportal.org>
     */
    public static function delClass($className)
    {
        $dbhandle = Injector :: getDB();
        $dbhandle->query("delete from Class where name='$className'");
        $dbhandle->query("delete from Injection where class='$className'");
        return true;
    }

    /**
     * delete an injection
     * 
     * @param String $key the key of injection
     * 
     * @return void
     * @author Rodrigue Le Gall <r.legall@rportal.org>
     */
    public static function delInjection($key)
    {
        $dbhandle = Injector :: getDB();
        $dbhandle->query("delete from Injection where key='$key'");
        return true;
    }

    /**
     * return the file path of a class or interface
     * 
     * @param String $className the class or interface name
     * 
     * @return String file path containing the class or interface
     * @throws RPUndefinedClassException
     */
    public static function getFile($className)
    {
        $dbhandle = Injector :: getDB();
        $result = $dbhandle->query("select file from Class where name='$className'");
        if ($line = $result->fetch()) {
            return $line['file'];
        }
        throw new RPUndefinedClassException('The class ' . $className . ' is undefined.');
    }
}